/*
 * Copyright 2011 myTDev.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mytdev.application;

import com.mytdev.application.service.ApplicationServiceLoader;
import com.mytdev.application.service.ServiceDescriptor;
import com.mytdev.application.service.ServiceException;
import com.mytdev.application.service.ServiceProvider;
import com.mytdev.commons.util.service.ServiceLookup;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The application core engine.
 * @author Yann D'Isanto
 */
public final class ApplicationEngine implements ApplicationServiceLoader {

    private static final Logger LOG = LoggerFactory.getLogger(ApplicationEngine.class);
    private ApplicationMetaData applicationMetaData;
    private Map<String, ServiceDescriptor> services;
    private Class<? extends JFrame> mainView;
    private Map<String, String> properties;

    public ApplicationEngine(ApplicationMetaData applicationMetaData, Map<String, ServiceDescriptor> services, Class<? extends JFrame> mainView, Map<String, String> properties) {
        this.applicationMetaData = applicationMetaData;
        this.services = services;
        this.mainView = mainView;
        this.properties = properties;
    }

    public ApplicationMetaData getApplicationMetaData() {
        return applicationMetaData;
    }

    @Override
    public String[] getAvailableServices() {
        return services.keySet().toArray(new String[services.size()]);
    }

    public Class<? extends JFrame> getMainView() {
        return mainView;
    }

    public String getProperty(Enum<?> key) {
        return getProperty(key.toString());
    }

    public String getProperty(String key) {
        return properties.get(key);
    }

    @Override
    public Object getService(String name) throws ServiceException {
        ServiceDescriptor descriptor = services.get(name);
        ServiceProvider<?> provider = getServiceProvider(descriptor.getProvider());
        if (provider == null) {
            throw new ServiceException("service provider " + descriptor.getProvider().getName() + " not found: ");
        }
        provider.setProperties(descriptor.getProperties());
        return provider.getService();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getService(Class<T> serviceType, String name) throws ServiceException {
        T service = null;
        ServiceDescriptor descriptor = services.get(name);
        if (serviceType.isAssignableFrom(descriptor.getServiceType())) {
            Class<? extends ServiceProvider<T>> providerType = (Class<? extends ServiceProvider<T>>) descriptor.getProvider();
            ServiceProvider<T> provider = getServiceProvider(providerType);
            if (provider == null) {
                throw new ServiceException("service provider not found (provider class might not be declared as a java service): " + providerType.getName());
            }
            provider.setProperties(descriptor.getProperties());
            service = provider.getService();
        } else {
            throw new ServiceException("invalid service type");
        }
        return service;
    }

    @Override
    public ServiceDescriptor getServiceDescriptor(String name) {
        return services.get(name);
    }

    @Override
    public List<String> getServiceDependencies(String name) throws ServiceException {
        ServiceDescriptor descriptor = services.get(name);
        return descriptor.getDependencies();
    }

    private <P extends ServiceProvider<?>> P getServiceProvider(Class<P> provider) {
        P p = null;
        if (provider.isInterface() || Modifier.isAbstract(provider.getModifiers())) {
            p = ServiceLookup.lookup(provider);
        }
        if (p == null) {
            p = ServiceLookup.lookup(ServiceProvider.class, provider);
        }
        return p;
    }
}
